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