--- /dev/null 2015-10-08 07:25:10.000000000 -1000 +++ new/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotConstantReflectionProvider.java 2015-10-08 07:25:10.000000000 -1000 @@ -0,0 +1,369 @@ +/* + * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.vm.ci.hotspot; + +import static jdk.vm.ci.hotspot.HotSpotConstantReflectionProvider.Options.*; + +import java.lang.reflect.*; + +import jdk.vm.ci.meta.*; +import jdk.vm.ci.options.*; + +/** + * HotSpot implementation of {@link ConstantReflectionProvider}. + */ +public class HotSpotConstantReflectionProvider implements ConstantReflectionProvider, HotSpotProxified { + + static class Options { + //@formatter:off + @Option(help = "Constant fold final fields with default values.", type = OptionType.Debug) + public static final OptionValue TrustFinalDefaultFields = new OptionValue<>(true); + //@formatter:on + } + + protected final HotSpotJVMCIRuntimeProvider runtime; + protected final HotSpotMethodHandleAccessProvider methodHandleAccess; + protected final HotSpotMemoryAccessProviderImpl memoryAccess; + + public HotSpotConstantReflectionProvider(HotSpotJVMCIRuntimeProvider runtime) { + this.runtime = runtime; + this.methodHandleAccess = new HotSpotMethodHandleAccessProvider(this); + this.memoryAccess = new HotSpotMemoryAccessProviderImpl(runtime); + } + + public MethodHandleAccessProvider getMethodHandleAccess() { + return methodHandleAccess; + } + + @Override + public MemoryAccessProvider getMemoryAccessProvider() { + return memoryAccess; + } + + @Override + public boolean isEmbeddable(Constant constant) { + return true; + } + + @Override + public Boolean constantEquals(Constant x, Constant y) { + if (x == y) { + return true; + } else if (x instanceof HotSpotObjectConstantImpl) { + return y instanceof HotSpotObjectConstantImpl && ((HotSpotObjectConstantImpl) x).object() == ((HotSpotObjectConstantImpl) y).object(); + } else { + return x.equals(y); + } + } + + @Override + public Integer readArrayLength(JavaConstant array) { + if (array.getJavaKind() != JavaKind.Object || array.isNull()) { + return null; + } + + Object arrayObject = ((HotSpotObjectConstantImpl) array).object(); + if (!arrayObject.getClass().isArray()) { + return null; + } + return Array.getLength(arrayObject); + } + + public JavaConstant readConstantArrayElement(JavaConstant array, int index) { + if (array instanceof HotSpotObjectConstantImpl && ((HotSpotObjectConstantImpl) array).getStableDimension() > 0) { + JavaConstant element = readArrayElement(array, index); + if (element != null && (((HotSpotObjectConstantImpl) array).isDefaultStable() || !element.isDefaultForKind())) { + return element; + } + } + return null; + } + + /** + * Try to convert {@code offset} into an an index into {@code array}. + * + * @return the computed index or -1 if the offset isn't within the array + */ + private int indexForOffset(JavaConstant array, long offset) { + if (array.getJavaKind() != JavaKind.Object || array.isNull()) { + return -1; + } + Class componentType = ((HotSpotObjectConstantImpl) array).object().getClass().getComponentType(); + JavaKind kind = runtime.getHostJVMCIBackend().getMetaAccess().lookupJavaType(componentType).getJavaKind(); + int arraybase = runtime.getArrayBaseOffset(kind); + int scale = runtime.getArrayIndexScale(kind); + if (offset < arraybase) { + return -1; + } + long index = offset - arraybase; + if (index % scale != 0) { + return -1; + } + long result = index / scale; + if (result >= Integer.MAX_VALUE) { + return -1; + } + return (int) result; + } + + public JavaConstant readConstantArrayElementForOffset(JavaConstant array, long offset) { + if (array instanceof HotSpotObjectConstantImpl && ((HotSpotObjectConstantImpl) array).getStableDimension() > 0) { + return readConstantArrayElement(array, indexForOffset(array, offset)); + } + return null; + } + + @Override + public JavaConstant readArrayElement(JavaConstant array, int index) { + if (array.getJavaKind() != JavaKind.Object || array.isNull()) { + return null; + } + Object a = ((HotSpotObjectConstantImpl) array).object(); + + if (index < 0 || index >= Array.getLength(a)) { + return null; + } + + if (a instanceof Object[]) { + Object element = ((Object[]) a)[index]; + if (((HotSpotObjectConstantImpl) array).getStableDimension() > 1) { + return HotSpotObjectConstantImpl.forStableArray(element, ((HotSpotObjectConstantImpl) array).getStableDimension() - 1, ((HotSpotObjectConstantImpl) array).isDefaultStable()); + } else { + return HotSpotObjectConstantImpl.forObject(element); + } + } else { + return JavaConstant.forBoxedPrimitive(Array.get(a, index)); + } + } + + /** + * Check if the constant is a boxed value that is guaranteed to be cached by the platform. + * Otherwise the generated code might be the only reference to the boxed value and since object + * references from nmethods are weak this can cause GC problems. + * + * @param source + * @return true if the box is cached + */ + private static boolean isBoxCached(JavaConstant source) { + switch (source.getJavaKind()) { + case Boolean: + return true; + case Char: + return source.asInt() <= 127; + case Byte: + case Short: + case Int: + return source.asInt() >= -128 && source.asInt() <= 127; + case Long: + return source.asLong() >= -128 && source.asLong() <= 127; + case Float: + case Double: + return false; + default: + throw new IllegalArgumentException("unexpected kind " + source.getJavaKind()); + } + } + + @Override + public JavaConstant boxPrimitive(JavaConstant source) { + if (!source.getJavaKind().isPrimitive() || !isBoxCached(source)) { + return null; + } + return HotSpotObjectConstantImpl.forObject(source.asBoxedPrimitive()); + } + + @Override + public JavaConstant unboxPrimitive(JavaConstant source) { + if (!source.getJavaKind().isObject()) { + return null; + } + if (source.isNull()) { + return null; + } + return JavaConstant.forBoxedPrimitive(((HotSpotObjectConstantImpl) source).object()); + } + + public JavaConstant forString(String value) { + return HotSpotObjectConstantImpl.forObject(value); + } + + @Override + public ResolvedJavaType asJavaType(Constant constant) { + if (constant instanceof HotSpotObjectConstant) { + Object obj = ((HotSpotObjectConstantImpl) constant).object(); + if (obj instanceof Class) { + return runtime.getHostJVMCIBackend().getMetaAccess().lookupJavaType((Class) obj); + } + } + if (constant instanceof HotSpotMetaspaceConstant) { + Object obj = HotSpotMetaspaceConstantImpl.getMetaspaceObject(constant); + if (obj instanceof HotSpotResolvedObjectTypeImpl) { + return (ResolvedJavaType) obj; + } + } + return null; + } + + private static final String SystemClassName = "Ljava/lang/System;"; + + /** + * Determines if a static field is constant for the purpose of + * {@link #readConstantFieldValue(JavaField, JavaConstant)}. + */ + protected boolean isStaticFieldConstant(HotSpotResolvedJavaField staticField) { + if (staticField.isFinal() || staticField.isStable()) { + ResolvedJavaType holder = staticField.getDeclaringClass(); + if (holder.isInitialized() && !holder.getName().equals(SystemClassName)) { + return true; + } + } + return false; + } + + /** + * Determines if a value read from a {@code final} instance field is considered constant. The + * implementation in {@link HotSpotConstantReflectionProvider} returns true if {@code value} is + * not the {@link JavaConstant#isDefaultForKind default value} for its kind or if + * {@link Options#TrustFinalDefaultFields} is true. + * + * @param value a value read from a {@code final} instance field + * @param receiverClass the {@link Object#getClass() class} of object from which the + * {@code value} was read + */ + protected boolean isFinalInstanceFieldValueConstant(JavaConstant value, Class receiverClass) { + return !value.isDefaultForKind() || TrustFinalDefaultFields.getValue(); + } + + /** + * Determines if a value read from a {@link Stable} instance field is considered constant. The + * implementation in {@link HotSpotConstantReflectionProvider} returns true if {@code value} is + * not the {@link JavaConstant#isDefaultForKind default value} for its kind. + * + * @param value a value read from a {@link Stable} field + * @param receiverClass the {@link Object#getClass() class} of object from which the + * {@code value} was read + */ + protected boolean isStableInstanceFieldValueConstant(JavaConstant value, Class receiverClass) { + return !value.isDefaultForKind(); + } + + /** + * {@inheritDoc} + *

+ * The {@code value} field in {@link OptionValue} is considered constant if the type of + * {@code receiver} is (assignable to) {@link StableOptionValue}. + */ + public JavaConstant readConstantFieldValue(JavaField field, JavaConstant receiver) { + HotSpotResolvedJavaField hotspotField = (HotSpotResolvedJavaField) field; + + if (hotspotField.isStatic()) { + if (isStaticFieldConstant(hotspotField)) { + JavaConstant value = readFieldValue(field, receiver); + if (hotspotField.isFinal() || !value.isDefaultForKind()) { + return value; + } + } + } else { + /* + * for non-static final fields, we must assume that they are only initialized if they + * have a non-default value. + */ + Object object = receiver.isNull() ? null : ((HotSpotObjectConstantImpl) receiver).object(); + + // Canonicalization may attempt to process an unsafe read before + // processing a guard (e.g. a null check or a type check) for this read + // so we need to check the object being read + if (object != null) { + if (hotspotField.isFinal()) { + if (hotspotField.isInObject(object)) { + JavaConstant value = readFieldValue(field, receiver); + if (isFinalInstanceFieldValueConstant(value, object.getClass())) { + return value; + } + } + } else if (hotspotField.isStable()) { + if (hotspotField.isInObject(object)) { + JavaConstant value = readFieldValue(field, receiver); + if (isStableInstanceFieldValueConstant(value, object.getClass())) { + return value; + } + } + } else { + Class clazz = object.getClass(); + if (StableOptionValue.class.isAssignableFrom(clazz)) { + if (hotspotField.isInObject(object) && hotspotField.getName().equals("value")) { + StableOptionValue option = (StableOptionValue) object; + return HotSpotObjectConstantImpl.forObject(option.getValue()); + } + } + } + } + } + return null; + } + + public JavaConstant readFieldValue(JavaField field, JavaConstant receiver) { + HotSpotResolvedJavaField hotspotField = (HotSpotResolvedJavaField) field; + if (!hotspotField.isStable()) { + return readNonStableFieldValue(field, receiver); + } else { + return readStableFieldValue(field, receiver, false); + } + } + + private JavaConstant readNonStableFieldValue(JavaField field, JavaConstant receiver) { + HotSpotResolvedJavaField hotspotField = (HotSpotResolvedJavaField) field; + if (hotspotField.isStatic()) { + HotSpotResolvedJavaType holder = (HotSpotResolvedJavaType) hotspotField.getDeclaringClass(); + if (holder.isInitialized()) { + return memoryAccess.readUnsafeConstant(hotspotField.getJavaKind(), HotSpotObjectConstantImpl.forObject(holder.mirror()), hotspotField.offset()); + } + } else { + if (receiver.isNonNull() && hotspotField.isInObject(((HotSpotObjectConstantImpl) receiver).object())) { + return memoryAccess.readUnsafeConstant(hotspotField.getJavaKind(), receiver, hotspotField.offset()); + } + } + return null; + } + + public JavaConstant readStableFieldValue(JavaField field, JavaConstant receiver, boolean isDefaultStable) { + JavaConstant fieldValue = readNonStableFieldValue(field, receiver); + if (fieldValue.isNonNull()) { + JavaType declaredType = field.getType(); + if (declaredType.getComponentType() != null) { + int stableDimension = getArrayDimension(declaredType); + return HotSpotObjectConstantImpl.forStableArray(((HotSpotObjectConstantImpl) fieldValue).object(), stableDimension, isDefaultStable); + } + } + return fieldValue; + } + + private static int getArrayDimension(JavaType type) { + int dimensions = 0; + JavaType componentType = type; + while ((componentType = componentType.getComponentType()) != null) { + dimensions++; + } + return dimensions; + } +}