001/* 002 * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. 008 * 009 * This code is distributed in the hope that it will be useful, but WITHOUT 010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 011 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 012 * version 2 for more details (a copy is included in the LICENSE file that 013 * accompanied this code). 014 * 015 * You should have received a copy of the GNU General Public License version 016 * 2 along with this work; if not, write to the Free Software Foundation, 017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 018 * 019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 020 * or visit www.oracle.com if you need additional information or have any 021 * questions. 022 */ 023package com.oracle.graal.compiler.common; 024 025import static jdk.internal.jvmci.common.UnsafeAccess.*; 026 027import java.util.*; 028 029import jdk.internal.jvmci.common.*; 030import sun.misc.*; 031 032/** 033 * Describes fields in a class, primarily for access via {@link Unsafe}. 034 */ 035public class Fields { 036 037 /** 038 * Offsets used with {@link Unsafe} to access the fields. 039 */ 040 protected final long[] offsets; 041 042 /** 043 * The names of the fields. 044 */ 045 private final String[] names; 046 047 /** 048 * The types of the fields. 049 */ 050 private final Class<?>[] types; 051 052 private final Class<?>[] declaringClasses; 053 054 public static Fields forClass(Class<?> clazz, Class<?> endClazz, boolean includeTransient, FieldsScanner.CalcOffset calcOffset) { 055 FieldsScanner scanner = new FieldsScanner(calcOffset == null ? new FieldsScanner.DefaultCalcOffset() : calcOffset); 056 scanner.scan(clazz, endClazz, includeTransient); 057 return new Fields(scanner.data); 058 } 059 060 public Fields(ArrayList<? extends FieldsScanner.FieldInfo> fields) { 061 Collections.sort(fields); 062 this.offsets = new long[fields.size()]; 063 this.names = new String[offsets.length]; 064 this.types = new Class[offsets.length]; 065 this.declaringClasses = new Class[offsets.length]; 066 int index = 0; 067 for (FieldsScanner.FieldInfo f : fields) { 068 offsets[index] = f.offset; 069 names[index] = f.name; 070 types[index] = f.type; 071 declaringClasses[index] = f.declaringClass; 072 index++; 073 } 074 } 075 076 /** 077 * Gets the number of fields represented by this object. 078 */ 079 public int getCount() { 080 return offsets.length; 081 } 082 083 public static void translateInto(Fields fields, ArrayList<FieldsScanner.FieldInfo> infos) { 084 for (int index = 0; index < fields.getCount(); index++) { 085 infos.add(new FieldsScanner.FieldInfo(fields.offsets[index], fields.names[index], fields.types[index], fields.declaringClasses[index])); 086 } 087 } 088 089 /** 090 * Function enabling an object field value to be replaced with another value when being copied 091 * within {@link Fields#copy(Object, Object, ObjectTransformer)}. 092 */ 093 @FunctionalInterface 094 public interface ObjectTransformer { 095 Object apply(int index, Object from); 096 } 097 098 /** 099 * Copies fields from {@code from} to {@code to}, both of which must be of the same type. 100 * 101 * @param from the object from which the fields should be copied 102 * @param to the object to which the fields should be copied 103 */ 104 public void copy(Object from, Object to) { 105 copy(from, to, null); 106 } 107 108 /** 109 * Copies fields from {@code from} to {@code to}, both of which must be of the same type. 110 * 111 * @param from the object from which the fields should be copied 112 * @param to the object to which the fields should be copied 113 * @param trans function to applied to object field values as they are copied. If {@code null}, 114 * the value is copied unchanged. 115 */ 116 public void copy(Object from, Object to, ObjectTransformer trans) { 117 assert from.getClass() == to.getClass(); 118 for (int index = 0; index < offsets.length; index++) { 119 long offset = offsets[index]; 120 Class<?> type = types[index]; 121 if (type.isPrimitive()) { 122 if (type == Integer.TYPE) { 123 unsafe.putInt(to, offset, unsafe.getInt(from, offset)); 124 } else if (type == Long.TYPE) { 125 unsafe.putLong(to, offset, unsafe.getLong(from, offset)); 126 } else if (type == Boolean.TYPE) { 127 unsafe.putBoolean(to, offset, unsafe.getBoolean(from, offset)); 128 } else if (type == Float.TYPE) { 129 unsafe.putFloat(to, offset, unsafe.getFloat(from, offset)); 130 } else if (type == Double.TYPE) { 131 unsafe.putDouble(to, offset, unsafe.getDouble(from, offset)); 132 } else if (type == Short.TYPE) { 133 unsafe.putShort(to, offset, unsafe.getShort(from, offset)); 134 } else if (type == Character.TYPE) { 135 unsafe.putChar(to, offset, unsafe.getChar(from, offset)); 136 } else if (type == Byte.TYPE) { 137 unsafe.putByte(to, offset, unsafe.getByte(from, offset)); 138 } else { 139 assert false : "unhandled property type: " + type; 140 } 141 } else { 142 Object obj = unsafe.getObject(from, offset); 143 unsafe.putObject(to, offset, trans == null ? obj : trans.apply(index, obj)); 144 } 145 } 146 } 147 148 /** 149 * Gets the value of a field for a given object. 150 * 151 * @param object the object whose field is to be read 152 * @param index the index of the field (between 0 and {@link #getCount()}) 153 * @return the value of the specified field which will be boxed if the field type is primitive 154 */ 155 public Object get(Object object, int index) { 156 long offset = offsets[index]; 157 Class<?> type = types[index]; 158 Object value = null; 159 if (type.isPrimitive()) { 160 if (type == Integer.TYPE) { 161 value = unsafe.getInt(object, offset); 162 } else if (type == Long.TYPE) { 163 value = unsafe.getLong(object, offset); 164 } else if (type == Boolean.TYPE) { 165 value = unsafe.getBoolean(object, offset); 166 } else if (type == Float.TYPE) { 167 value = unsafe.getFloat(object, offset); 168 } else if (type == Double.TYPE) { 169 value = unsafe.getDouble(object, offset); 170 } else if (type == Short.TYPE) { 171 value = unsafe.getShort(object, offset); 172 } else if (type == Character.TYPE) { 173 value = unsafe.getChar(object, offset); 174 } else if (type == Byte.TYPE) { 175 value = unsafe.getByte(object, offset); 176 } else { 177 assert false : "unhandled property type: " + type; 178 } 179 } else { 180 value = unsafe.getObject(object, offset); 181 } 182 return value; 183 } 184 185 /** 186 * Gets the value of a field for a given object. 187 * 188 * @param object the object whose field is to be read 189 * @param index the index of the field (between 0 and {@link #getCount()}) 190 * @return the value of the specified field which will be boxed if the field type is primitive 191 */ 192 public long getRawPrimitive(Object object, int index) { 193 long offset = offsets[index]; 194 Class<?> type = types[index]; 195 196 if (type == Integer.TYPE) { 197 return unsafe.getInt(object, offset); 198 } else if (type == Long.TYPE) { 199 return unsafe.getLong(object, offset); 200 } else if (type == Boolean.TYPE) { 201 return unsafe.getBoolean(object, offset) ? 1 : 0; 202 } else if (type == Float.TYPE) { 203 return Float.floatToRawIntBits(unsafe.getFloat(object, offset)); 204 } else if (type == Double.TYPE) { 205 return Double.doubleToRawLongBits(unsafe.getDouble(object, offset)); 206 } else if (type == Short.TYPE) { 207 return unsafe.getShort(object, offset); 208 } else if (type == Character.TYPE) { 209 return unsafe.getChar(object, offset); 210 } else if (type == Byte.TYPE) { 211 return unsafe.getByte(object, offset); 212 } else { 213 throw JVMCIError.shouldNotReachHere(); 214 } 215 } 216 217 /** 218 * Determines if a field in the domain of this object is the same as the field denoted by the 219 * same index in another {@link Fields} object. 220 */ 221 public boolean isSame(Fields other, int index) { 222 return other.offsets[index] == offsets[index]; 223 } 224 225 public long[] getOffsets() { 226 return offsets; 227 } 228 229 /** 230 * Gets the name of a field. 231 * 232 * @param index index of a field 233 */ 234 public String getName(int index) { 235 return names[index]; 236 } 237 238 /** 239 * Gets the type of a field. 240 * 241 * @param index index of a field 242 */ 243 public Class<?> getType(int index) { 244 return types[index]; 245 } 246 247 public Class<?> getDeclaringClass(int index) { 248 return declaringClasses[index]; 249 } 250 251 /** 252 * Checks that a given field is assignable from a given value. 253 * 254 * @param index the index of the field to check 255 * @param value a value that will be assigned to the field 256 */ 257 private boolean checkAssignableFrom(Object object, int index, Object value) { 258 assert value == null || getType(index).isAssignableFrom(value.getClass()) : String.format("Field %s.%s of type %s is not assignable from %s", object.getClass().getSimpleName(), 259 getName(index), getType(index).getSimpleName(), value.getClass().getSimpleName()); 260 return true; 261 } 262 263 public void set(Object object, int index, Object value) { 264 long offset = offsets[index]; 265 Class<?> type = types[index]; 266 if (type.isPrimitive()) { 267 if (type == Integer.TYPE) { 268 unsafe.putInt(object, offset, (Integer) value); 269 } else if (type == Long.TYPE) { 270 unsafe.putLong(object, offset, (Long) value); 271 } else if (type == Boolean.TYPE) { 272 unsafe.putBoolean(object, offset, (Boolean) value); 273 } else if (type == Float.TYPE) { 274 unsafe.putFloat(object, offset, (Float) value); 275 } else if (type == Double.TYPE) { 276 unsafe.putDouble(object, offset, (Double) value); 277 } else if (type == Short.TYPE) { 278 unsafe.putShort(object, offset, (Short) value); 279 } else if (type == Character.TYPE) { 280 unsafe.putChar(object, offset, (Character) value); 281 } else if (type == Byte.TYPE) { 282 unsafe.putByte(object, offset, (Byte) value); 283 } else { 284 assert false : "unhandled property type: " + type; 285 } 286 } else { 287 assert checkAssignableFrom(object, index, value); 288 unsafe.putObject(object, offset, value); 289 } 290 } 291 292 public void setRawPrimitive(Object object, int index, long value) { 293 long offset = offsets[index]; 294 Class<?> type = types[index]; 295 if (type == Integer.TYPE) { 296 unsafe.putInt(object, offset, (int) value); 297 } else if (type == Long.TYPE) { 298 unsafe.putLong(object, offset, value); 299 } else if (type == Boolean.TYPE) { 300 unsafe.putBoolean(object, offset, value != 0); 301 } else if (type == Float.TYPE) { 302 unsafe.putFloat(object, offset, Float.intBitsToFloat((int) value)); 303 } else if (type == Double.TYPE) { 304 unsafe.putDouble(object, offset, Double.longBitsToDouble(value)); 305 } else if (type == Short.TYPE) { 306 unsafe.putShort(object, offset, (short) value); 307 } else if (type == Character.TYPE) { 308 unsafe.putChar(object, offset, (char) value); 309 } else if (type == Byte.TYPE) { 310 unsafe.putByte(object, offset, (byte) value); 311 } else { 312 throw JVMCIError.shouldNotReachHere(); 313 } 314 } 315 316 @Override 317 public String toString() { 318 StringBuilder sb = new StringBuilder(getClass().getSimpleName()).append('['); 319 appendFields(sb); 320 return sb.append(']').toString(); 321 } 322 323 public void appendFields(StringBuilder sb) { 324 for (int i = 0; i < offsets.length; i++) { 325 sb.append(i == 0 ? "" : ", ").append(getName(i)).append('@').append(offsets[i]); 326 } 327 } 328 329 public boolean getBoolean(Object n, int i) { 330 assert types[i] == boolean.class; 331 return unsafe.getBoolean(n, offsets[i]); 332 } 333 334 public byte getByte(Object n, int i) { 335 assert types[i] == byte.class; 336 return unsafe.getByte(n, offsets[i]); 337 } 338 339 public short getShort(Object n, int i) { 340 assert types[i] == short.class; 341 return unsafe.getShort(n, offsets[i]); 342 } 343 344 public char getChar(Object n, int i) { 345 assert types[i] == char.class; 346 return unsafe.getChar(n, offsets[i]); 347 } 348 349 public int getInt(Object n, int i) { 350 assert types[i] == int.class; 351 return unsafe.getInt(n, offsets[i]); 352 } 353 354 public long getLong(Object n, int i) { 355 assert types[i] == long.class; 356 return unsafe.getLong(n, offsets[i]); 357 } 358 359 public float getFloat(Object n, int i) { 360 assert types[i] == float.class; 361 return unsafe.getFloat(n, offsets[i]); 362 } 363 364 public double getDouble(Object n, int i) { 365 assert types[i] == double.class; 366 return unsafe.getDouble(n, offsets[i]); 367 } 368 369 public Object getObject(Object object, int i) { 370 assert !types[i].isPrimitive(); 371 return unsafe.getObject(object, offsets[i]); 372 } 373 374 public void putObject(Object object, int i, Object value) { 375 assert checkAssignableFrom(object, i, value); 376 unsafe.putObject(object, offsets[i], value); 377 } 378}