1 /*
   2  * Copyright (c) 2011, 2015, 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 jdk.vm.ci.hotspot;
  24 
  25 import static jdk.vm.ci.hotspot.HotSpotConstantReflectionProvider.Options.*;
  26 
  27 import java.lang.reflect.*;
  28 
  29 import jdk.vm.ci.meta.*;
  30 import jdk.vm.ci.options.*;
  31 
  32 /**
  33  * HotSpot implementation of {@link ConstantReflectionProvider}.
  34  */
  35 public class HotSpotConstantReflectionProvider implements ConstantReflectionProvider, HotSpotProxified {
  36 
  37     static class Options {
  38         //@formatter:off
  39         @Option(help = "Constant fold final fields with default values.", type = OptionType.Debug)
  40         public static final OptionValue<Boolean> TrustFinalDefaultFields = new OptionValue<>(true);
  41         //@formatter:on
  42     }
  43 
  44     protected final HotSpotJVMCIRuntimeProvider runtime;
  45     protected final HotSpotMethodHandleAccessProvider methodHandleAccess;
  46     protected final HotSpotMemoryAccessProviderImpl memoryAccess;
  47 
  48     public HotSpotConstantReflectionProvider(HotSpotJVMCIRuntimeProvider runtime) {
  49         this.runtime = runtime;
  50         this.methodHandleAccess = new HotSpotMethodHandleAccessProvider(this);
  51         this.memoryAccess = new HotSpotMemoryAccessProviderImpl(runtime);
  52     }
  53 
  54     public MethodHandleAccessProvider getMethodHandleAccess() {
  55         return methodHandleAccess;
  56     }
  57 
  58     @Override
  59     public MemoryAccessProvider getMemoryAccessProvider() {
  60         return memoryAccess;
  61     }
  62 
  63     @Override
  64     public boolean isEmbeddable(Constant constant) {
  65         return true;
  66     }
  67 
  68     @Override
  69     public Boolean constantEquals(Constant x, Constant y) {
  70         if (x == y) {
  71             return true;
  72         } else if (x instanceof HotSpotObjectConstantImpl) {
  73             return y instanceof HotSpotObjectConstantImpl && ((HotSpotObjectConstantImpl) x).object() == ((HotSpotObjectConstantImpl) y).object();
  74         } else {
  75             return x.equals(y);
  76         }
  77     }
  78 
  79     @Override
  80     public Integer readArrayLength(JavaConstant array) {
  81         if (array.getJavaKind() != JavaKind.Object || array.isNull()) {
  82             return null;
  83         }
  84 
  85         Object arrayObject = ((HotSpotObjectConstantImpl) array).object();
  86         if (!arrayObject.getClass().isArray()) {
  87             return null;
  88         }
  89         return Array.getLength(arrayObject);
  90     }
  91 
  92     public JavaConstant readConstantArrayElement(JavaConstant array, int index) {
  93         if (array instanceof HotSpotObjectConstantImpl && ((HotSpotObjectConstantImpl) array).getStableDimension() > 0) {
  94             JavaConstant element = readArrayElement(array, index);
  95             if (element != null && (((HotSpotObjectConstantImpl) array).isDefaultStable() || !element.isDefaultForKind())) {
  96                 return element;
  97             }
  98         }
  99         return null;
 100     }
 101 
 102     /**
 103      * Try to convert {@code offset} into an an index into {@code array}.
 104      *
 105      * @return the computed index or -1 if the offset isn't within the array
 106      */
 107     private int indexForOffset(JavaConstant array, long offset) {
 108         if (array.getJavaKind() != JavaKind.Object || array.isNull()) {
 109             return -1;
 110         }
 111         Class<?> componentType = ((HotSpotObjectConstantImpl) array).object().getClass().getComponentType();
 112         JavaKind kind = runtime.getHostJVMCIBackend().getMetaAccess().lookupJavaType(componentType).getJavaKind();
 113         int arraybase = runtime.getArrayBaseOffset(kind);
 114         int scale = runtime.getArrayIndexScale(kind);
 115         if (offset < arraybase) {
 116             return -1;
 117         }
 118         long index = offset - arraybase;
 119         if (index % scale != 0) {
 120             return -1;
 121         }
 122         long result = index / scale;
 123         if (result >= Integer.MAX_VALUE) {
 124             return -1;
 125         }
 126         return (int) result;
 127     }
 128 
 129     public JavaConstant readConstantArrayElementForOffset(JavaConstant array, long offset) {
 130         if (array instanceof HotSpotObjectConstantImpl && ((HotSpotObjectConstantImpl) array).getStableDimension() > 0) {
 131             return readConstantArrayElement(array, indexForOffset(array, offset));
 132         }
 133         return null;
 134     }
 135 
 136     @Override
 137     public JavaConstant readArrayElement(JavaConstant array, int index) {
 138         if (array.getJavaKind() != JavaKind.Object || array.isNull()) {
 139             return null;
 140         }
 141         Object a = ((HotSpotObjectConstantImpl) array).object();
 142 
 143         if (index < 0 || index >= Array.getLength(a)) {
 144             return null;
 145         }
 146 
 147         if (a instanceof Object[]) {
 148             Object element = ((Object[]) a)[index];
 149             if (((HotSpotObjectConstantImpl) array).getStableDimension() > 1) {
 150                 return HotSpotObjectConstantImpl.forStableArray(element, ((HotSpotObjectConstantImpl) array).getStableDimension() - 1, ((HotSpotObjectConstantImpl) array).isDefaultStable());
 151             } else {
 152                 return HotSpotObjectConstantImpl.forObject(element);
 153             }
 154         } else {
 155             return JavaConstant.forBoxedPrimitive(Array.get(a, index));
 156         }
 157     }
 158 
 159     /**
 160      * Check if the constant is a boxed value that is guaranteed to be cached by the platform.
 161      * Otherwise the generated code might be the only reference to the boxed value and since object
 162      * references from nmethods are weak this can cause GC problems.
 163      *
 164      * @param source
 165      * @return true if the box is cached
 166      */
 167     private static boolean isBoxCached(JavaConstant source) {
 168         switch (source.getJavaKind()) {
 169             case Boolean:
 170                 return true;
 171             case Char:
 172                 return source.asInt() <= 127;
 173             case Byte:
 174             case Short:
 175             case Int:
 176                 return source.asInt() >= -128 && source.asInt() <= 127;
 177             case Long:
 178                 return source.asLong() >= -128 && source.asLong() <= 127;
 179             case Float:
 180             case Double:
 181                 return false;
 182             default:
 183                 throw new IllegalArgumentException("unexpected kind " + source.getJavaKind());
 184         }
 185     }
 186 
 187     @Override
 188     public JavaConstant boxPrimitive(JavaConstant source) {
 189         if (!source.getJavaKind().isPrimitive() || !isBoxCached(source)) {
 190             return null;
 191         }
 192         return HotSpotObjectConstantImpl.forObject(source.asBoxedPrimitive());
 193     }
 194 
 195     @Override
 196     public JavaConstant unboxPrimitive(JavaConstant source) {
 197         if (!source.getJavaKind().isObject()) {
 198             return null;
 199         }
 200         if (source.isNull()) {
 201             return null;
 202         }
 203         return JavaConstant.forBoxedPrimitive(((HotSpotObjectConstantImpl) source).object());
 204     }
 205 
 206     public JavaConstant forString(String value) {
 207         return HotSpotObjectConstantImpl.forObject(value);
 208     }
 209 
 210     @Override
 211     public ResolvedJavaType asJavaType(Constant constant) {
 212         if (constant instanceof HotSpotObjectConstant) {
 213             Object obj = ((HotSpotObjectConstantImpl) constant).object();
 214             if (obj instanceof Class) {
 215                 return runtime.getHostJVMCIBackend().getMetaAccess().lookupJavaType((Class<?>) obj);
 216             }
 217         }
 218         if (constant instanceof HotSpotMetaspaceConstant) {
 219             Object obj = HotSpotMetaspaceConstantImpl.getMetaspaceObject(constant);
 220             if (obj instanceof HotSpotResolvedObjectTypeImpl) {
 221                 return (ResolvedJavaType) obj;
 222             }
 223         }
 224         return null;
 225     }
 226 
 227     private static final String SystemClassName = "Ljava/lang/System;";
 228 
 229     /**
 230      * Determines if a static field is constant for the purpose of
 231      * {@link #readConstantFieldValue(JavaField, JavaConstant)}.
 232      */
 233     protected boolean isStaticFieldConstant(HotSpotResolvedJavaField staticField) {
 234         if (staticField.isFinal() || staticField.isStable()) {
 235             ResolvedJavaType holder = staticField.getDeclaringClass();
 236             if (holder.isInitialized() && !holder.getName().equals(SystemClassName)) {
 237                 return true;
 238             }
 239         }
 240         return false;
 241     }
 242 
 243     /**
 244      * Determines if a value read from a {@code final} instance field is considered constant. The
 245      * implementation in {@link HotSpotConstantReflectionProvider} returns true if {@code value} is
 246      * not the {@link JavaConstant#isDefaultForKind default value} for its kind or if
 247      * {@link Options#TrustFinalDefaultFields} is true.
 248      *
 249      * @param value a value read from a {@code final} instance field
 250      * @param receiverClass the {@link Object#getClass() class} of object from which the
 251      *            {@code value} was read
 252      */
 253     protected boolean isFinalInstanceFieldValueConstant(JavaConstant value, Class<?> receiverClass) {
 254         return !value.isDefaultForKind() || TrustFinalDefaultFields.getValue();
 255     }
 256 
 257     /**
 258      * Determines if a value read from a {@link Stable} instance field is considered constant. The
 259      * implementation in {@link HotSpotConstantReflectionProvider} returns true if {@code value} is
 260      * not the {@link JavaConstant#isDefaultForKind default value} for its kind.
 261      *
 262      * @param value a value read from a {@link Stable} field
 263      * @param receiverClass the {@link Object#getClass() class} of object from which the
 264      *            {@code value} was read
 265      */
 266     protected boolean isStableInstanceFieldValueConstant(JavaConstant value, Class<?> receiverClass) {
 267         return !value.isDefaultForKind();
 268     }
 269 
 270     /**
 271      * {@inheritDoc}
 272      * <p>
 273      * The {@code value} field in {@link OptionValue} is considered constant if the type of
 274      * {@code receiver} is (assignable to) {@link StableOptionValue}.
 275      */
 276     public JavaConstant readConstantFieldValue(JavaField field, JavaConstant receiver) {
 277         HotSpotResolvedJavaField hotspotField = (HotSpotResolvedJavaField) field;
 278 
 279         if (hotspotField.isStatic()) {
 280             if (isStaticFieldConstant(hotspotField)) {
 281                 JavaConstant value = readFieldValue(field, receiver);
 282                 if (hotspotField.isFinal() || !value.isDefaultForKind()) {
 283                     return value;
 284                 }
 285             }
 286         } else {
 287             /*
 288              * for non-static final fields, we must assume that they are only initialized if they
 289              * have a non-default value.
 290              */
 291             Object object = receiver.isNull() ? null : ((HotSpotObjectConstantImpl) receiver).object();
 292 
 293             // Canonicalization may attempt to process an unsafe read before
 294             // processing a guard (e.g. a null check or a type check) for this read
 295             // so we need to check the object being read
 296             if (object != null) {
 297                 if (hotspotField.isFinal()) {
 298                     if (hotspotField.isInObject(object)) {
 299                         JavaConstant value = readFieldValue(field, receiver);
 300                         if (isFinalInstanceFieldValueConstant(value, object.getClass())) {
 301                             return value;
 302                         }
 303                     }
 304                 } else if (hotspotField.isStable()) {
 305                     if (hotspotField.isInObject(object)) {
 306                         JavaConstant value = readFieldValue(field, receiver);
 307                         if (isStableInstanceFieldValueConstant(value, object.getClass())) {
 308                             return value;
 309                         }
 310                     }
 311                 } else {
 312                     Class<?> clazz = object.getClass();
 313                     if (StableOptionValue.class.isAssignableFrom(clazz)) {
 314                         if (hotspotField.isInObject(object) && hotspotField.getName().equals("value")) {
 315                             StableOptionValue<?> option = (StableOptionValue<?>) object;
 316                             return HotSpotObjectConstantImpl.forObject(option.getValue());
 317                         }
 318                     }
 319                 }
 320             }
 321         }
 322         return null;
 323     }
 324 
 325     public JavaConstant readFieldValue(JavaField field, JavaConstant receiver) {
 326         HotSpotResolvedJavaField hotspotField = (HotSpotResolvedJavaField) field;
 327         if (!hotspotField.isStable()) {
 328             return readNonStableFieldValue(field, receiver);
 329         } else {
 330             return readStableFieldValue(field, receiver, false);
 331         }
 332     }
 333 
 334     private JavaConstant readNonStableFieldValue(JavaField field, JavaConstant receiver) {
 335         HotSpotResolvedJavaField hotspotField = (HotSpotResolvedJavaField) field;
 336         if (hotspotField.isStatic()) {
 337             HotSpotResolvedJavaType holder = (HotSpotResolvedJavaType) hotspotField.getDeclaringClass();
 338             if (holder.isInitialized()) {
 339                 return memoryAccess.readUnsafeConstant(hotspotField.getJavaKind(), HotSpotObjectConstantImpl.forObject(holder.mirror()), hotspotField.offset());
 340             }
 341         } else {
 342             if (receiver.isNonNull() && hotspotField.isInObject(((HotSpotObjectConstantImpl) receiver).object())) {
 343                 return memoryAccess.readUnsafeConstant(hotspotField.getJavaKind(), receiver, hotspotField.offset());
 344             }
 345         }
 346         return null;
 347     }
 348 
 349     public JavaConstant readStableFieldValue(JavaField field, JavaConstant receiver, boolean isDefaultStable) {
 350         JavaConstant fieldValue = readNonStableFieldValue(field, receiver);
 351         if (fieldValue.isNonNull()) {
 352             JavaType declaredType = field.getType();
 353             if (declaredType.getComponentType() != null) {
 354                 int stableDimension = getArrayDimension(declaredType);
 355                 return HotSpotObjectConstantImpl.forStableArray(((HotSpotObjectConstantImpl) fieldValue).object(), stableDimension, isDefaultStable);
 356             }
 357         }
 358         return fieldValue;
 359     }
 360 
 361     private static int getArrayDimension(JavaType type) {
 362         int dimensions = 0;
 363         JavaType componentType = type;
 364         while ((componentType = componentType.getComponentType()) != null) {
 365             dimensions++;
 366         }
 367         return dimensions;
 368     }
 369 }