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}