1 /*
   2  * Copyright (c) 2011, 2016, 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 import static jdk.vm.ci.hotspot.UnsafeAccess.UNSAFE;
  28 
  29 import java.lang.reflect.Array;
  30 
  31 import jdk.vm.ci.common.JVMCIError;
  32 import jdk.vm.ci.meta.Constant;
  33 import jdk.vm.ci.meta.JavaConstant;
  34 import jdk.vm.ci.meta.JavaKind;
  35 import jdk.vm.ci.meta.MemoryAccessProvider;
  36 import jdk.vm.ci.meta.MetaAccessProvider;
  37 import jdk.vm.ci.meta.PrimitiveConstant;
  38 import jdk.vm.ci.meta.ResolvedJavaField;
  39 import jdk.vm.ci.meta.ResolvedJavaType;
  40 
  41 /**
  42  * HotSpot implementation of {@link MemoryAccessProvider}.
  43  */
  44 class HotSpotMemoryAccessProviderImpl implements HotSpotMemoryAccessProvider {
  45 
  46     protected final HotSpotJVMCIRuntimeProvider runtime;
  47 
  48     HotSpotMemoryAccessProviderImpl(HotSpotJVMCIRuntimeProvider runtime) {
  49         this.runtime = runtime;
  50     }
  51 
  52     /**
  53      * Gets the object boxed by {@code base} that is about to have a value of kind {@code kind} read
  54      * from it at the offset {@code displacement}.
  55      *
  56      * @param base constant value containing the base address for a pending read
  57      * @return {@code null} if {@code base} does not box an object otherwise the object boxed in
  58      *         {@code base}
  59      */
  60     private Object asObject(Constant base, JavaKind kind, long displacement) {
  61         if (base instanceof HotSpotObjectConstantImpl) {
  62             HotSpotObjectConstantImpl constant = (HotSpotObjectConstantImpl) base;
  63             HotSpotResolvedObjectType type = constant.getType();
  64             Object object = constant.object();
  65             checkRead(kind, displacement, type, object);
  66             return object;
  67         }
  68         return null;
  69     }
  70 
  71     /**
  72      * Offset of injected {@code java.lang.Class::oop_size} field. No need to make {@code volatile}
  73      * as initialization is idempotent.
  74      */
  75     private long oopSizeOffset;
  76 
  77     private static int computeOopSizeOffset(HotSpotJVMCIRuntimeProvider runtime) {
  78         MetaAccessProvider metaAccess = runtime.getHostJVMCIBackend().getMetaAccess();
  79         ResolvedJavaType staticType = metaAccess.lookupJavaType(Class.class);
  80         for (ResolvedJavaField f : staticType.getInstanceFields(false)) {
  81             if (f.getName().equals("oop_size")) {
  82                 int offset = ((HotSpotResolvedJavaField) f).offset();
  83                 assert offset != 0 : "not expecting offset of java.lang.Class::oop_size to be 0";
  84                 return offset;
  85             }
  86         }
  87         throw new JVMCIError("Could not find injected java.lang.Class::oop_size field");
  88     }
  89 
  90     private boolean checkRead(JavaKind kind, long displacement, HotSpotResolvedObjectType type, Object object) {
  91         if (type.isArray()) {
  92             ResolvedJavaType componentType = type.getComponentType();
  93             JavaKind componentKind = componentType.getJavaKind();
  94             final int headerSize = getArrayBaseOffset(componentKind);
  95             int sizeOfElement = getArrayIndexScale(componentKind);
  96             int length = Array.getLength(object);
  97             long arrayEnd = headerSize + (sizeOfElement * length);
  98             boolean aligned = ((displacement - headerSize) % sizeOfElement) == 0;
  99             if (displacement < 0 || displacement > (arrayEnd - sizeOfElement) || (kind == JavaKind.Object && !aligned)) {
 100                 int index = (int) ((displacement - headerSize) / sizeOfElement);
 101                 throw new IllegalArgumentException("Unsafe array access: reading element of kind " + kind +
 102                                 " at offset " + displacement + " (index ~ " + index + ") in " +
 103                                 type.toJavaName() + " object of length " + length);
 104             }
 105         } else if (kind != JavaKind.Object) {
 106             long size;
 107             if (object instanceof Class) {
 108                 if (oopSizeOffset == 0) {
 109                     oopSizeOffset = computeOopSizeOffset(runtime);
 110                 }
 111                 int wordSize = runtime.getHostJVMCIBackend().getCodeCache().getTarget().wordSize;
 112                 size = UNSAFE.getInt(object, oopSizeOffset) * wordSize;
 113             } else {
 114                 size = Math.abs(type.instanceSize());
 115             }
 116             int bytesToRead = kind.getByteCount();
 117             if (displacement + bytesToRead > size || displacement < 0) {
 118                 throw new IllegalArgumentException("Unsafe access: reading " + bytesToRead + " bytes at offset " + displacement + " in " +
 119                                 type.toJavaName() + " object of size " + size);
 120             }
 121         } else {
 122             ResolvedJavaField field = type.findInstanceFieldWithOffset(displacement, JavaKind.Object);
 123             if (field == null && object instanceof Class) {
 124                 // Read of a static field
 125                 MetaAccessProvider metaAccess = runtime.getHostJVMCIBackend().getMetaAccess();
 126                 HotSpotResolvedObjectTypeImpl staticFieldsHolder = (HotSpotResolvedObjectTypeImpl) metaAccess.lookupJavaType((Class<?>) object);
 127                 field = staticFieldsHolder.findStaticFieldWithOffset(displacement, JavaKind.Object);
 128             }
 129             if (field == null) {
 130                 throw new IllegalArgumentException("Unsafe object access: field not found for read of kind Object" +
 131                                 " at offset " + displacement + " in " + type.toJavaName() + " object");
 132             }
 133             if (field.getJavaKind() != JavaKind.Object) {
 134                 throw new IllegalArgumentException("Unsafe object access: field " + field.format("%H.%n:%T") + " not of expected kind Object" +
 135                                 " at offset " + displacement + " in " + type.toJavaName() + " object");
 136             }
 137         }
 138         return true;
 139     }
 140 
 141     private static long asRawPointer(Constant base) {
 142         if (base instanceof HotSpotMetaspaceConstantImpl) {
 143             MetaspaceWrapperObject meta = HotSpotMetaspaceConstantImpl.getMetaspaceObject(base);
 144             return meta.getMetaspacePointer();
 145         } else if (base instanceof PrimitiveConstant) {
 146             PrimitiveConstant prim = (PrimitiveConstant) base;
 147             if (prim.getJavaKind().isNumericInteger()) {
 148                 return prim.asLong();
 149             }
 150         }
 151         throw new IllegalArgumentException(String.valueOf(base));
 152     }
 153 
 154     private long readRawValue(Constant baseConstant, long displacement, JavaKind kind, int bits) {
 155         Object base = asObject(baseConstant, kind, displacement);
 156         if (base != null) {
 157             switch (bits) {
 158                 case Byte.SIZE:
 159                     return UNSAFE.getByte(base, displacement);
 160                 case Short.SIZE:
 161                     return UNSAFE.getShort(base, displacement);
 162                 case Integer.SIZE:
 163                     return UNSAFE.getInt(base, displacement);
 164                 case Long.SIZE:
 165                     return UNSAFE.getLong(base, displacement);
 166                 default:
 167                     throw new IllegalArgumentException(String.valueOf(bits));
 168             }
 169         } else {
 170             long pointer = asRawPointer(baseConstant);
 171             switch (bits) {
 172                 case Byte.SIZE:
 173                     return UNSAFE.getByte(pointer + displacement);
 174                 case Short.SIZE:
 175                     return UNSAFE.getShort(pointer + displacement);
 176                 case Integer.SIZE:
 177                     return UNSAFE.getInt(pointer + displacement);
 178                 case Long.SIZE:
 179                     return UNSAFE.getLong(pointer + displacement);
 180                 default:
 181                     throw new IllegalArgumentException(String.valueOf(bits));
 182             }
 183         }
 184     }
 185 
 186     private boolean verifyReadRawObject(Object expected, Constant base, long displacement) {
 187         if (base instanceof HotSpotMetaspaceConstant) {
 188             MetaspaceWrapperObject metaspaceObject = HotSpotMetaspaceConstantImpl.getMetaspaceObject(base);
 189             if (metaspaceObject instanceof HotSpotResolvedObjectTypeImpl) {
 190                 if (displacement == runtime.getConfig().classMirrorHandleOffset) {
 191                     assert expected == ((HotSpotResolvedObjectTypeImpl) metaspaceObject).mirror();
 192                 }
 193             }
 194         }
 195         return true;
 196     }
 197 
 198     private Object readRawObject(Constant baseConstant, long initialDisplacement, boolean compressed) {
 199         long displacement = initialDisplacement;
 200         Object ret;
 201         Object base = asObject(baseConstant, JavaKind.Object, displacement);
 202         if (base == null) {
 203             assert !compressed;
 204             displacement += asRawPointer(baseConstant);
 205             ret = UNSAFE.getUncompressedObject(displacement);
 206             assert verifyReadRawObject(ret, baseConstant, initialDisplacement);
 207         } else {
 208             assert runtime.getConfig().useCompressedOops == compressed;
 209             ret = UNSAFE.getObject(base, displacement);
 210         }
 211         return ret;
 212     }
 213 
 214     JavaConstant readFieldValue(HotSpotResolvedJavaField field, Object obj) {
 215         assert obj != null;
 216         assert !field.isStatic() || obj instanceof Class;
 217         long displacement = field.offset();
 218         assert checkRead(field.getJavaKind(), displacement, (HotSpotResolvedObjectType) runtime.getHostJVMCIBackend().getMetaAccess().lookupJavaType(obj.getClass()), obj);
 219         if (field.getJavaKind() == JavaKind.Object) {
 220             Object o = UNSAFE.getObject(obj, displacement);
 221             return HotSpotObjectConstantImpl.forObject(o);
 222         } else {
 223             JavaKind kind = field.getJavaKind();
 224             switch (kind) {
 225                 case Boolean:
 226                     return JavaConstant.forBoolean(UNSAFE.getBoolean(obj, displacement));
 227                 case Byte:
 228                     return JavaConstant.forByte(UNSAFE.getByte(obj, displacement));
 229                 case Char:
 230                     return JavaConstant.forChar(UNSAFE.getChar(obj, displacement));
 231                 case Short:
 232                     return JavaConstant.forShort(UNSAFE.getShort(obj, displacement));
 233                 case Int:
 234                     return JavaConstant.forInt(UNSAFE.getInt(obj, displacement));
 235                 case Long:
 236                     return JavaConstant.forLong(UNSAFE.getLong(obj, displacement));
 237                 case Float:
 238                     return JavaConstant.forFloat(UNSAFE.getFloat(obj, displacement));
 239                 case Double:
 240                     return JavaConstant.forDouble(UNSAFE.getDouble(obj, displacement));
 241                 default:
 242                     throw new IllegalArgumentException("Unsupported kind: " + kind);
 243             }
 244         }
 245     }
 246 
 247     @Override
 248     public JavaConstant readPrimitiveConstant(JavaKind kind, Constant baseConstant, long initialDisplacement, int bits) {
 249         try {
 250             long rawValue = readRawValue(baseConstant, initialDisplacement, kind, bits);
 251             switch (kind) {
 252                 case Boolean:
 253                     return JavaConstant.forBoolean(rawValue != 0);
 254                 case Byte:
 255                     return JavaConstant.forByte((byte) rawValue);
 256                 case Char:
 257                     return JavaConstant.forChar((char) rawValue);
 258                 case Short:
 259                     return JavaConstant.forShort((short) rawValue);
 260                 case Int:
 261                     return JavaConstant.forInt((int) rawValue);
 262                 case Long:
 263                     return JavaConstant.forLong(rawValue);
 264                 case Float:
 265                     return JavaConstant.forFloat(Float.intBitsToFloat((int) rawValue));
 266                 case Double:
 267                     return JavaConstant.forDouble(Double.longBitsToDouble(rawValue));
 268                 default:
 269                     throw new IllegalArgumentException("Unsupported kind: " + kind);
 270             }
 271         } catch (NullPointerException e) {
 272             return null;
 273         }
 274     }
 275 
 276     @Override
 277     public JavaConstant readObjectConstant(Constant base, long displacement) {
 278         if (base instanceof HotSpotObjectConstantImpl) {
 279             Object o = readRawObject(base, displacement, runtime.getConfig().useCompressedOops);
 280             return HotSpotObjectConstantImpl.forObject(o);
 281         }
 282         if (base instanceof HotSpotMetaspaceConstant) {
 283             MetaspaceWrapperObject metaspaceObject = HotSpotMetaspaceConstantImpl.getMetaspaceObject(base);
 284             if (metaspaceObject instanceof HotSpotResolvedObjectTypeImpl) {
 285                  if (displacement == runtime.getConfig().classMirrorHandleOffset) {
 286                     // Klass::_java_mirror is valid for all Klass* values
 287                     return HotSpotObjectConstantImpl.forObject(((HotSpotResolvedObjectTypeImpl) metaspaceObject).mirror());
 288                  }
 289              } else {
 290                  throw new IllegalArgumentException(String.valueOf(metaspaceObject));
 291              }
 292         }
 293         return null;
 294     }
 295 
 296     @Override
 297     public JavaConstant readNarrowOopConstant(Constant base, long displacement) {
 298         return HotSpotObjectConstantImpl.forObject(readRawObject(base, displacement, true), true);
 299     }
 300 
 301     private HotSpotResolvedObjectTypeImpl readKlass(Constant base, long displacement, boolean compressed) {
 302         assert (base instanceof HotSpotMetaspaceConstantImpl) || (base instanceof HotSpotObjectConstantImpl) : base.getClass();
 303         Object baseObject = (base instanceof HotSpotMetaspaceConstantImpl) ? ((HotSpotMetaspaceConstantImpl) base).asResolvedJavaType() : ((HotSpotObjectConstantImpl) base).object();
 304         return runtime.getCompilerToVM().getResolvedJavaType(baseObject, displacement, compressed);
 305     }
 306 
 307     @Override
 308     public Constant readKlassPointerConstant(Constant base, long displacement) {
 309         HotSpotResolvedObjectTypeImpl klass = readKlass(base, displacement, false);
 310         if (klass == null) {
 311             return JavaConstant.NULL_POINTER;
 312         }
 313         return HotSpotMetaspaceConstantImpl.forMetaspaceObject(klass, false);
 314     }
 315 
 316     @Override
 317     public Constant readNarrowKlassPointerConstant(Constant base, long displacement) {
 318         HotSpotResolvedObjectTypeImpl klass = readKlass(base, displacement, true);
 319         if (klass == null) {
 320             return HotSpotCompressedNullConstant.COMPRESSED_NULL;
 321         }
 322         return HotSpotMetaspaceConstantImpl.forMetaspaceObject(klass, true);
 323     }
 324 
 325     @Override
 326     public Constant readMethodPointerConstant(Constant base, long displacement) {
 327         assert (base instanceof HotSpotObjectConstantImpl);
 328         Object baseObject = ((HotSpotObjectConstantImpl) base).object();
 329         HotSpotResolvedJavaMethodImpl method = runtime.getCompilerToVM().getResolvedJavaMethod(baseObject, displacement);
 330         return HotSpotMetaspaceConstantImpl.forMetaspaceObject(method, false);
 331     }
 332 }