1 /*
   2  * Copyright (c) 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 /*
  27  * @test
  28  * @summary tests on constant folding of unsafe get operations
  29  * @library /testlibrary /../../test/lib
  30  * @run main/bootclasspath -XX:+UnlockDiagnosticVMOptions
  31  *                   -Xbatch -XX:-TieredCompilation
  32  *                   -XX:+FoldStableValues
  33  *                   -XX:+UseUnalignedAccesses
  34  *                   java.lang.invoke.UnsafeGetConstantField
  35  * @run main/bootclasspath -XX:+UnlockDiagnosticVMOptions
  36  *                   -Xbatch -XX:-TieredCompilation
  37  *                   -XX:+FoldStableValues
  38  *                   -XX:-UseUnalignedAccesses
  39  *                   java.lang.invoke.UnsafeGetConstantField
  40  */
  41 package java.lang.invoke;
  42 
  43 import jdk.internal.org.objectweb.asm.*;
  44 import jdk.test.lib.Asserts;
  45 import jdk.test.lib.Utils;
  46 import sun.misc.Unsafe;
  47 import static jdk.internal.org.objectweb.asm.Opcodes.*;
  48 
  49 public class UnsafeGetConstantField {
  50     static final Class<?> THIS_CLASS = UnsafeGetConstantField.class;
  51 
  52     static final Unsafe U = Utils.getUnsafe();
  53 
  54     public static void main(String[] args) {
  55         testUnsafeGetAddress();
  56         testUnsafeGetField();
  57         testUnsafeGetFieldUnaligned();
  58         System.out.println("TEST PASSED");
  59     }
  60 
  61     static final long nativeAddr = U.allocateMemory(16);
  62     static void testUnsafeGetAddress() {
  63         long cookie = 0x12345678L;
  64         U.putAddress(nativeAddr, cookie);
  65         for (int i = 0; i < 20_000; i++) {
  66             Asserts.assertEquals(checkGetAddress(), cookie);
  67         }
  68     }
  69     @DontInline
  70     static long checkGetAddress() {
  71         return U.getAddress(nativeAddr);
  72     }
  73 
  74     static void testUnsafeGetField() {
  75         int[] testedFlags = new int[] { 0, ACC_STATIC, ACC_FINAL, (ACC_STATIC | ACC_FINAL) };
  76         boolean[] boolValues = new boolean[] { false, true };
  77         String[] modes = new String[] { "", "Volatile" };
  78 
  79         for (JavaType t : JavaType.values()) {
  80             for (int flags : testedFlags) {
  81                 for (boolean stable : boolValues) {
  82                     for (boolean hasDefaultValue : boolValues) {
  83                         for (String suffix : modes) {
  84                             runTest(t, flags, stable, hasDefaultValue, suffix);
  85                         }
  86                     }
  87                 }
  88             }
  89         }
  90     }
  91 
  92     static void testUnsafeGetFieldUnaligned() {
  93         JavaType[] types = new JavaType[] { JavaType.S, JavaType.C, JavaType.I, JavaType.J };
  94         int[] testedFlags = new int[] { 0, ACC_STATIC, ACC_FINAL, (ACC_STATIC | ACC_FINAL) };
  95         boolean[] boolValues = new boolean[] { false, true };
  96 
  97         for (JavaType t : types) {
  98             for (int flags : testedFlags) {
  99                 for (boolean stable : boolValues) {
 100                     for (boolean hasDefaultValue : boolValues) {
 101                         runTest(t, flags, stable, hasDefaultValue, "Unaligned");
 102                     }
 103                 }
 104             }
 105         }
 106     }
 107 
 108     static void runTest(JavaType t, int flags, boolean stable, boolean hasDefaultValue, String postfix) {
 109         Generator g = new Generator(t, flags, stable, hasDefaultValue, postfix);
 110         Test test = g.generate();
 111         System.out.printf("type=%s flags=%d stable=%b default=%b post=%s\n",
 112                           t.typeName, flags, stable, hasDefaultValue, postfix);
 113         // Trigger compilation
 114         for (int i = 0; i < 20_000; i++) {
 115             Asserts.assertEQ(test.testDirect(), test.testUnsafe());
 116         }
 117     }
 118 
 119     interface Test {
 120         Object testDirect();
 121         Object testUnsafe();
 122     }
 123 
 124     enum JavaType {
 125         Z("Boolean", true),
 126         B("Byte", new Byte((byte)-1)),
 127         S("Short", new Short((short)-1)),
 128         C("Char", Character.MAX_VALUE),
 129         I("Int", -1),
 130         J("Long", -1L),
 131         F("Float", -1F),
 132         D("Double", -1D),
 133         L("Object", new Object());
 134 
 135         String typeName;
 136         Object value;
 137         String wrapper;
 138         JavaType(String name, Object value) {
 139             this.typeName = name;
 140             this.value = value;
 141             this.wrapper = internalName(value.getClass());
 142         }
 143 
 144         String desc() {
 145             if (this == JavaType.L) {
 146                 return "Ljava/lang/Object;";
 147             } else {
 148                 return toString();
 149             }
 150         }
 151     }
 152 
 153     static String internalName(Class cls) {
 154         return cls.getName().replace('.', '/');
 155     }
 156     static String descriptor(Class cls) {
 157         return String.format("L%s;", internalName(cls));
 158     }
 159 
 160     /**
 161      * Sample generated class:
 162      * static class T1 implements Test {
 163      *   final int f = -1;
 164      *   static final long FIELD_OFFSET;
 165      *   static final T1 t = new T1();
 166      *   static {
 167      *     FIELD_OFFSET = U.objectFieldOffset(T1.class.getDeclaredField("f"));
 168      *   }
 169      *   public Object testDirect()  { return t.f; }
 170      *   public Object testUnsafe()  { return U.getInt(t, FIELD_OFFSET); }
 171      * }
 172      */
 173     static class Generator {
 174         static final String FIELD_NAME = "f";
 175         static final String UNSAFE_NAME = internalName(Unsafe.class);
 176         static final String UNSAFE_DESC = descriptor(Unsafe.class);
 177 
 178         final JavaType type;
 179         final int flags;
 180         final boolean stable;
 181         final boolean hasDefaultValue;
 182         final String nameSuffix;
 183 
 184         final String className;
 185         final String classDesc;
 186         final String fieldDesc;
 187 
 188         Generator(JavaType t, int flags, boolean stable, boolean hasDefaultValue, String suffix) {
 189             this.type = t;
 190             this.flags = flags;
 191             this.stable = stable;
 192             this.hasDefaultValue = hasDefaultValue;
 193             this.nameSuffix = suffix;
 194 
 195             fieldDesc = type.desc();
 196             className = String.format("%s$Test%s%s__f=%d__s=%b__d=%b", internalName(THIS_CLASS), type.typeName,
 197                                       suffix, flags, stable, hasDefaultValue);
 198             classDesc = String.format("L%s;", className);
 199         }
 200 
 201         byte[] generateClassFile() {
 202             ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
 203             cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER, className, null, "java/lang/Object",
 204                     new String[]{ internalName(Test.class) });
 205 
 206             // Declare fields
 207             cw.visitField(ACC_FINAL | ACC_STATIC, "t", classDesc, null, null).visitEnd();
 208             cw.visitField(ACC_FINAL | ACC_STATIC, "FIELD_OFFSET", "J", null, null).visitEnd();
 209             cw.visitField(ACC_FINAL | ACC_STATIC, "U", UNSAFE_DESC, null, null).visitEnd();
 210             if (isStatic()) {
 211                 cw.visitField(ACC_FINAL | ACC_STATIC, "STATIC_BASE", "Ljava/lang/Object;", null, null).visitEnd();
 212             }
 213 
 214             FieldVisitor fv = cw.visitField(flags, FIELD_NAME, fieldDesc, null, null);
 215             if (stable) {
 216                 fv.visitAnnotation(descriptor(Stable.class), true);
 217             }
 218             fv.visitEnd();
 219 
 220             // Methods
 221             {   // <init>
 222                 MethodVisitor mv = cw.visitMethod(0, "<init>", "()V", null, null);
 223                 mv.visitCode();
 224 
 225                 mv.visitVarInsn(ALOAD, 0);
 226                 mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
 227                 if (!isStatic()) {
 228                     initField(mv);
 229                 }
 230                 mv.visitInsn(RETURN);
 231 
 232                 mv.visitMaxs(0, 0);
 233                 mv.visitEnd();
 234             }
 235 
 236             {   // public Object testDirect() { return t.f; }
 237                 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "testDirect", "()Ljava/lang/Object;", null, null);
 238                 mv.visitCode();
 239 
 240                 getFieldValue(mv);
 241                 wrapResult(mv);
 242                 mv.visitInsn(ARETURN);
 243 
 244                 mv.visitMaxs(0, 0);
 245                 mv.visitEnd();
 246             }
 247 
 248             {   // public Object testUnsafe() { return U.getInt(t, FIELD_OFFSET); }
 249                 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "testUnsafe", "()Ljava/lang/Object;", null, null);
 250                 mv.visitCode();
 251 
 252                 getFieldValueUnsafe(mv);
 253                 wrapResult(mv);
 254                 mv.visitInsn(ARETURN);
 255 
 256                 mv.visitMaxs(0, 0);
 257                 mv.visitEnd();
 258             }
 259 
 260             {   // <clinit>
 261                 MethodVisitor mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
 262                 mv.visitCode();
 263 
 264                 // Cache Unsafe instance
 265                 mv.visitMethodInsn(INVOKESTATIC, UNSAFE_NAME, "getUnsafe", "()"+UNSAFE_DESC, false);
 266                 mv.visitFieldInsn(PUTSTATIC, className, "U", UNSAFE_DESC);
 267 
 268                 // Create test object instance
 269                 mv.visitTypeInsn(NEW, className);
 270                 mv.visitInsn(DUP);
 271                 mv.visitMethodInsn(INVOKESPECIAL, className, "<init>", "()V", false);
 272                 mv.visitFieldInsn(PUTSTATIC, className, "t", classDesc);
 273 
 274                 // Compute field offset
 275                 getUnsafe(mv);
 276                 getField(mv);
 277                 mv.visitMethodInsn(INVOKEVIRTUAL, UNSAFE_NAME, (isStatic() ? "staticFieldOffset" : "objectFieldOffset"),
 278                         "(Ljava/lang/reflect/Field;)J", false);
 279                 mv.visitFieldInsn(PUTSTATIC, className, "FIELD_OFFSET", "J");
 280 
 281                 // Compute base offset for static field
 282                 if (isStatic()) {
 283                     getUnsafe(mv);
 284                     getField(mv);
 285                     mv.visitMethodInsn(INVOKEVIRTUAL, UNSAFE_NAME, "staticFieldBase", "(Ljava/lang/reflect/Field;)Ljava/lang/Object;", false);
 286                     mv.visitFieldInsn(PUTSTATIC, className, "STATIC_BASE", "Ljava/lang/Object;");
 287                     initField(mv);
 288                 }
 289 
 290                 mv.visitInsn(RETURN);
 291                 mv.visitMaxs(0, 0);
 292                 mv.visitEnd();
 293             }
 294 
 295             return cw.toByteArray();
 296         }
 297 
 298         Test generate() {
 299             byte[] classFile = generateClassFile();
 300             Class<?> c = U.defineClass(className, classFile, 0, classFile.length, THIS_CLASS.getClassLoader(), null);
 301             try {
 302                 return (Test) c.newInstance();
 303             } catch(Exception e) {
 304                 throw new Error(e);
 305             }
 306         }
 307 
 308         boolean isStatic() {
 309             return (flags & ACC_STATIC) > 0;
 310         }
 311         boolean isFinal() {
 312             return (flags & ACC_FINAL) > 0;
 313         }
 314         void getUnsafe(MethodVisitor mv) {
 315             mv.visitFieldInsn(GETSTATIC, className, "U", UNSAFE_DESC);
 316         }
 317         void getField(MethodVisitor mv) {
 318             mv.visitLdcInsn(Type.getType(classDesc));
 319             mv.visitLdcInsn(FIELD_NAME);
 320             mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getDeclaredField", "(Ljava/lang/String;)Ljava/lang/reflect/Field;", false);
 321         }
 322         void getFieldValue(MethodVisitor mv) {
 323             if (isStatic()) {
 324                 mv.visitFieldInsn(GETSTATIC, className, FIELD_NAME, fieldDesc);
 325             } else {
 326                 mv.visitFieldInsn(GETSTATIC, className, "t", classDesc);
 327                 mv.visitFieldInsn(GETFIELD, className, FIELD_NAME, fieldDesc);
 328             }
 329         }
 330         void getFieldValueUnsafe(MethodVisitor mv) {
 331             getUnsafe(mv);
 332             if (isStatic()) {
 333                 mv.visitFieldInsn(GETSTATIC, className, "STATIC_BASE", "Ljava/lang/Object;");
 334             } else {
 335                 mv.visitFieldInsn(GETSTATIC, className, "t", classDesc);
 336             }
 337             mv.visitFieldInsn(GETSTATIC, className, "FIELD_OFFSET", "J");
 338             String name = "get" + type.typeName + nameSuffix;
 339             mv.visitMethodInsn(INVOKEVIRTUAL, UNSAFE_NAME, name, "(Ljava/lang/Object;J)" + type.desc(), false);
 340         }
 341         void wrapResult(MethodVisitor mv) {
 342             if (type != JavaType.L) {
 343                 String desc = String.format("(%s)L%s;", type.desc(), type.wrapper);
 344                 mv.visitMethodInsn(INVOKESTATIC, type.wrapper, "valueOf", desc, false);
 345             }
 346         }
 347         void initField(MethodVisitor mv) {
 348             if (hasDefaultValue) {
 349                 return; // Nothing to do
 350             }
 351             if (!isStatic()) {
 352                 mv.visitVarInsn(ALOAD, 0);
 353             }
 354             switch (type) {
 355                 case L: {
 356                     mv.visitTypeInsn(NEW, "java/lang/Object");
 357                     mv.visitInsn(DUP);
 358                     mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
 359 
 360                     break;
 361                 }
 362                 default: {
 363                     mv.visitLdcInsn(type.value);
 364                     break;
 365                 }
 366             }
 367             mv.visitFieldInsn((isStatic() ? PUTSTATIC : PUTFIELD), className, FIELD_NAME, fieldDesc);
 368         }
 369     }
 370 }