1 /*
   2  * Copyright (c) 2020, 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 
  24 /* @test
  25  * @summary Ensure that sun.misc.Unsafe::objectFieldOffset and staticFieldOffset
  26  *          throw UnsupportedOperationException on Field of a hidden class
  27  * @modules jdk.unsupported
  28  * @run main UnsafeFieldOffsets
  29  */
  30 
  31 import sun.misc.Unsafe;
  32 
  33 import java.io.IOException;
  34 import java.io.UncheckedIOException;
  35 import java.lang.invoke.MethodHandles;
  36 import java.lang.reflect.Field;
  37 import java.nio.file.Files;
  38 import java.nio.file.Path;
  39 import java.nio.file.Paths;
  40 
  41 public class UnsafeFieldOffsets {
  42     static class Fields {
  43         static final Object STATIC_FINAL = new Object();
  44         static Object STATIC_NON_FINAL = new Object();
  45         final Object FINAL = new Object();
  46         Object NON_FINAL = new Object();
  47     }
  48 
  49     private static Unsafe UNSAFE = getUnsafe();
  50     private static final Class<?> HIDDEN_CLASS = defineHiddenClass();
  51 
  52     private static Unsafe getUnsafe() {
  53         try {
  54             Field f = Unsafe.class.getDeclaredField("theUnsafe");
  55             f.setAccessible(true);
  56             return (Unsafe) f.get(null);
  57         } catch (ReflectiveOperationException e) {
  58             throw new RuntimeException(e);
  59         }
  60     }
  61 
  62     private static Class<?> defineHiddenClass() {
  63         String classes = System.getProperty("test.classes");
  64         Path cf = Paths.get(classes, "UnsafeFieldOffsets$Fields.class");
  65         try {
  66             byte[] bytes = Files.readAllBytes(cf);
  67             Class<?> c = MethodHandles.lookup().defineHiddenClass(bytes, true).lookupClass();
  68             if (!c.isHiddenClass())
  69                 throw new RuntimeException("Expected hidden class: " + c);
  70             return c;
  71         } catch (IOException e) {
  72             throw new UncheckedIOException(e);
  73         } catch (IllegalAccessException e) {
  74             throw new RuntimeException(e);
  75         }
  76     }
  77 
  78     public static void main(String[] args) throws Exception {
  79         // non-hidden class
  80         testStaticField(Fields.class, "STATIC_FINAL");
  81         testStaticField(Fields.class, "STATIC_NON_FINAL");
  82         testInstanceField(Fields.class, "FINAL");
  83         testInstanceField(Fields.class, "NON_FINAL");
  84 
  85         // hidden class
  86         testStaticField(HIDDEN_CLASS, "STATIC_FINAL");
  87         testStaticField(HIDDEN_CLASS, "STATIC_NON_FINAL");
  88         testInstanceField(HIDDEN_CLASS, "FINAL");
  89         testInstanceField(HIDDEN_CLASS, "NON_FINAL");
  90     }
  91 
  92     private static void testStaticField(Class<?> c, String name) throws Exception {
  93         Field f = c.getDeclaredField(name);
  94         try {
  95             UNSAFE.staticFieldOffset(f);
  96             assertNonHiddenClass(c);
  97         } catch (UnsupportedOperationException e) {
  98             assertHiddenClass(c);
  99         }
 100     }
 101 
 102     private static void testInstanceField(Class<?> c, String name) throws Exception {
 103         Field f = c.getDeclaredField(name);
 104         try {
 105             UNSAFE.objectFieldOffset(f);
 106             assertNonHiddenClass(c);
 107         } catch (UnsupportedOperationException e) {
 108             assertHiddenClass(c);
 109         }
 110     }
 111 
 112     private static void assertNonHiddenClass(Class<?> c) {
 113         if (c.isHiddenClass())
 114             throw new RuntimeException("Expected UOE but not thrown: " + c);
 115     }
 116 
 117     private static void assertHiddenClass(Class<?> c) {
 118         if (!c.isHiddenClass())
 119             throw new RuntimeException("Expected hidden class but is not: " + c);
 120     }
 121 }