001/*
002 * Copyright (c) 2011, 2015, 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 jdk.internal.jvmci.hotspot;
024
025import static jdk.internal.jvmci.hotspot.HotSpotConstantReflectionProvider.Options.*;
026
027import java.lang.reflect.*;
028
029import jdk.internal.jvmci.meta.*;
030import jdk.internal.jvmci.options.*;
031
032/**
033 * HotSpot implementation of {@link ConstantReflectionProvider}.
034 */
035public class HotSpotConstantReflectionProvider implements ConstantReflectionProvider, HotSpotProxified {
036
037    static class Options {
038        //@formatter:off
039        @Option(help = "Constant fold final fields with default values.", type = OptionType.Debug)
040        public static final OptionValue<Boolean> TrustFinalDefaultFields = new OptionValue<>(true);
041        //@formatter:on
042    }
043
044    protected final HotSpotJVMCIRuntimeProvider runtime;
045    protected final HotSpotMethodHandleAccessProvider methodHandleAccess;
046    protected final HotSpotMemoryAccessProviderImpl memoryAccess;
047
048    public HotSpotConstantReflectionProvider(HotSpotJVMCIRuntimeProvider runtime) {
049        this.runtime = runtime;
050        this.methodHandleAccess = new HotSpotMethodHandleAccessProvider(this);
051        this.memoryAccess = new HotSpotMemoryAccessProviderImpl(runtime);
052    }
053
054    public MethodHandleAccessProvider getMethodHandleAccess() {
055        return methodHandleAccess;
056    }
057
058    @Override
059    public MemoryAccessProvider getMemoryAccessProvider() {
060        return memoryAccess;
061    }
062
063    @Override
064    public Boolean constantEquals(Constant x, Constant y) {
065        if (x == y) {
066            return true;
067        } else if (x instanceof HotSpotObjectConstantImpl) {
068            return y instanceof HotSpotObjectConstantImpl && ((HotSpotObjectConstantImpl) x).object() == ((HotSpotObjectConstantImpl) y).object();
069        } else {
070            return x.equals(y);
071        }
072    }
073
074    @Override
075    public Integer readArrayLength(JavaConstant array) {
076        if (array.getKind() != Kind.Object || array.isNull()) {
077            return null;
078        }
079
080        Object arrayObject = ((HotSpotObjectConstantImpl) array).object();
081        if (!arrayObject.getClass().isArray()) {
082            return null;
083        }
084        return Array.getLength(arrayObject);
085    }
086
087    public JavaConstant readConstantArrayElement(JavaConstant array, int index) {
088        if (array instanceof HotSpotObjectConstantImpl && ((HotSpotObjectConstantImpl) array).getStableDimension() > 0) {
089            JavaConstant element = readArrayElement(array, index);
090            if (element != null && (((HotSpotObjectConstantImpl) array).isDefaultStable() || !element.isDefaultForKind())) {
091                return element;
092            }
093        }
094        return null;
095    }
096
097    /**
098     * Try to convert {@code offset} into an an index into {@code array}.
099     *
100     * @return the computed index or -1 if the offset isn't within the array
101     */
102    private int indexForOffset(JavaConstant array, long offset) {
103        if (array.getKind() != Kind.Object || array.isNull()) {
104            return -1;
105        }
106        Class<?> componentType = ((HotSpotObjectConstantImpl) array).object().getClass().getComponentType();
107        Kind kind = runtime.getHostJVMCIBackend().getMetaAccess().lookupJavaType(componentType).getKind();
108        int arraybase = runtime.getArrayBaseOffset(kind);
109        int scale = runtime.getArrayIndexScale(kind);
110        if (offset < arraybase) {
111            return -1;
112        }
113        long index = offset - arraybase;
114        if (index % scale != 0) {
115            return -1;
116        }
117        long result = index / scale;
118        if (result >= Integer.MAX_VALUE) {
119            return -1;
120        }
121        return (int) result;
122    }
123
124    public JavaConstant readConstantArrayElementForOffset(JavaConstant array, long offset) {
125        if (array instanceof HotSpotObjectConstantImpl && ((HotSpotObjectConstantImpl) array).getStableDimension() > 0) {
126            return readConstantArrayElement(array, indexForOffset(array, offset));
127        }
128        return null;
129    }
130
131    @Override
132    public JavaConstant readArrayElement(JavaConstant array, int index) {
133        if (array.getKind() != Kind.Object || array.isNull()) {
134            return null;
135        }
136        Object a = ((HotSpotObjectConstantImpl) array).object();
137
138        if (index < 0 || index >= Array.getLength(a)) {
139            return null;
140        }
141
142        if (a instanceof Object[]) {
143            Object element = ((Object[]) a)[index];
144            if (((HotSpotObjectConstantImpl) array).getStableDimension() > 1) {
145                return HotSpotObjectConstantImpl.forStableArray(element, ((HotSpotObjectConstantImpl) array).getStableDimension() - 1, ((HotSpotObjectConstantImpl) array).isDefaultStable());
146            } else {
147                return HotSpotObjectConstantImpl.forObject(element);
148            }
149        } else {
150            return JavaConstant.forBoxedPrimitive(Array.get(a, index));
151        }
152    }
153
154    /**
155     * Check if the constant is a boxed value that is guaranteed to be cached by the platform.
156     * Otherwise the generated code might be the only reference to the boxed value and since object
157     * references from nmethods are weak this can cause GC problems.
158     *
159     * @param source
160     * @return true if the box is cached
161     */
162    private static boolean isBoxCached(JavaConstant source) {
163        switch (source.getKind()) {
164            case Boolean:
165                return true;
166            case Char:
167                return source.asInt() <= 127;
168            case Byte:
169            case Short:
170            case Int:
171                return source.asInt() >= -128 && source.asInt() <= 127;
172            case Long:
173                return source.asLong() >= -128 && source.asLong() <= 127;
174            case Float:
175            case Double:
176                return false;
177            default:
178                throw new IllegalArgumentException("unexpected kind " + source.getKind());
179        }
180    }
181
182    @Override
183    public JavaConstant boxPrimitive(JavaConstant source) {
184        if (!source.getKind().isPrimitive() || !isBoxCached(source)) {
185            return null;
186        }
187        return HotSpotObjectConstantImpl.forObject(source.asBoxedPrimitive());
188    }
189
190    @Override
191    public JavaConstant unboxPrimitive(JavaConstant source) {
192        if (!source.getKind().isObject()) {
193            return null;
194        }
195        if (source.isNull()) {
196            return null;
197        }
198        return JavaConstant.forBoxedPrimitive(((HotSpotObjectConstantImpl) source).object());
199    }
200
201    public JavaConstant forString(String value) {
202        return HotSpotObjectConstantImpl.forObject(value);
203    }
204
205    @Override
206    public ResolvedJavaType asJavaType(Constant constant) {
207        if (constant instanceof HotSpotObjectConstant) {
208            Object obj = ((HotSpotObjectConstantImpl) constant).object();
209            if (obj instanceof Class) {
210                return runtime.getHostJVMCIBackend().getMetaAccess().lookupJavaType((Class<?>) obj);
211            }
212        }
213        if (constant instanceof HotSpotMetaspaceConstant) {
214            Object obj = HotSpotMetaspaceConstantImpl.getMetaspaceObject(constant);
215            if (obj instanceof HotSpotResolvedObjectTypeImpl) {
216                return (ResolvedJavaType) obj;
217            }
218        }
219        return null;
220    }
221
222    private static final String SystemClassName = "Ljava/lang/System;";
223
224    /**
225     * Determines if a static field is constant for the purpose of
226     * {@link #readConstantFieldValue(JavaField, JavaConstant)}.
227     */
228    protected boolean isStaticFieldConstant(HotSpotResolvedJavaField staticField) {
229        if (staticField.isFinal() || staticField.isStable()) {
230            ResolvedJavaType holder = staticField.getDeclaringClass();
231            if (holder.isInitialized() && !holder.getName().equals(SystemClassName)) {
232                return true;
233            }
234        }
235        return false;
236    }
237
238    /**
239     * Determines if a value read from a {@code final} instance field is considered constant. The
240     * implementation in {@link HotSpotConstantReflectionProvider} returns true if {@code value} is
241     * not the {@link JavaConstant#isDefaultForKind default value} for its kind or if
242     * {@link Options#TrustFinalDefaultFields} is true.
243     *
244     * @param value a value read from a {@code final} instance field
245     * @param receiverClass the {@link Object#getClass() class} of object from which the
246     *            {@code value} was read
247     */
248    protected boolean isFinalInstanceFieldValueConstant(JavaConstant value, Class<?> receiverClass) {
249        return !value.isDefaultForKind() || TrustFinalDefaultFields.getValue();
250    }
251
252    /**
253     * Determines if a value read from a {@link Stable} instance field is considered constant. The
254     * implementation in {@link HotSpotConstantReflectionProvider} returns true if {@code value} is
255     * not the {@link JavaConstant#isDefaultForKind default value} for its kind.
256     *
257     * @param value a value read from a {@link Stable} field
258     * @param receiverClass the {@link Object#getClass() class} of object from which the
259     *            {@code value} was read
260     */
261    protected boolean isStableInstanceFieldValueConstant(JavaConstant value, Class<?> receiverClass) {
262        return !value.isDefaultForKind();
263    }
264
265    /**
266     * {@inheritDoc}
267     * <p>
268     * The {@code value} field in {@link OptionValue} is considered constant if the type of
269     * {@code receiver} is (assignable to) {@link StableOptionValue}.
270     */
271    public JavaConstant readConstantFieldValue(JavaField field, JavaConstant receiver) {
272        HotSpotResolvedJavaField hotspotField = (HotSpotResolvedJavaField) field;
273
274        if (hotspotField.isStatic()) {
275            if (isStaticFieldConstant(hotspotField)) {
276                JavaConstant value = readFieldValue(field, receiver);
277                if (hotspotField.isFinal() || !value.isDefaultForKind()) {
278                    return value;
279                }
280            }
281        } else {
282            /*
283             * for non-static final fields, we must assume that they are only initialized if they
284             * have a non-default value.
285             */
286            Object object = receiver.isNull() ? null : ((HotSpotObjectConstantImpl) receiver).object();
287
288            // Canonicalization may attempt to process an unsafe read before
289            // processing a guard (e.g. a null check or a type check) for this read
290            // so we need to check the object being read
291            if (object != null) {
292                if (hotspotField.isFinal()) {
293                    if (hotspotField.isInObject(object)) {
294                        JavaConstant value = readFieldValue(field, receiver);
295                        if (isFinalInstanceFieldValueConstant(value, object.getClass())) {
296                            return value;
297                        }
298                    }
299                } else if (hotspotField.isStable()) {
300                    if (hotspotField.isInObject(object)) {
301                        JavaConstant value = readFieldValue(field, receiver);
302                        if (isStableInstanceFieldValueConstant(value, object.getClass())) {
303                            return value;
304                        }
305                    }
306                } else {
307                    Class<?> clazz = object.getClass();
308                    if (StableOptionValue.class.isAssignableFrom(clazz)) {
309                        if (hotspotField.isInObject(object) && hotspotField.getName().equals("value")) {
310                            StableOptionValue<?> option = (StableOptionValue<?>) object;
311                            return HotSpotObjectConstantImpl.forObject(option.getValue());
312                        }
313                    }
314                }
315            }
316        }
317        return null;
318    }
319
320    public JavaConstant readFieldValue(JavaField field, JavaConstant receiver) {
321        HotSpotResolvedJavaField hotspotField = (HotSpotResolvedJavaField) field;
322        if (!hotspotField.isStable()) {
323            return readNonStableFieldValue(field, receiver);
324        } else {
325            return readStableFieldValue(field, receiver, false);
326        }
327    }
328
329    private JavaConstant readNonStableFieldValue(JavaField field, JavaConstant receiver) {
330        HotSpotResolvedJavaField hotspotField = (HotSpotResolvedJavaField) field;
331        if (hotspotField.isStatic()) {
332            HotSpotResolvedJavaType holder = (HotSpotResolvedJavaType) hotspotField.getDeclaringClass();
333            if (holder.isInitialized()) {
334                return memoryAccess.readUnsafeConstant(hotspotField.getKind(), HotSpotObjectConstantImpl.forObject(holder.mirror()), hotspotField.offset());
335            }
336        } else {
337            if (receiver.isNonNull() && hotspotField.isInObject(((HotSpotObjectConstantImpl) receiver).object())) {
338                return memoryAccess.readUnsafeConstant(hotspotField.getKind(), receiver, hotspotField.offset());
339            }
340        }
341        return null;
342    }
343
344    public JavaConstant readStableFieldValue(JavaField field, JavaConstant receiver, boolean isDefaultStable) {
345        JavaConstant fieldValue = readNonStableFieldValue(field, receiver);
346        if (fieldValue.isNonNull()) {
347            JavaType declaredType = field.getType();
348            if (declaredType.getComponentType() != null) {
349                int stableDimension = getArrayDimension(declaredType);
350                return HotSpotObjectConstantImpl.forStableArray(((HotSpotObjectConstantImpl) fieldValue).object(), stableDimension, isDefaultStable);
351            }
352        }
353        return fieldValue;
354    }
355
356    private static int getArrayDimension(JavaType type) {
357        int dimensions = 0;
358        JavaType componentType = type;
359        while ((componentType = componentType.getComponentType()) != null) {
360            dimensions++;
361        }
362        return dimensions;
363    }
364}