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}