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 /** 25 * @test 26 * @compile src/Fields.java 27 * @run testng/othervm UnreflectTest 28 * @summary Test Lookup::unreflectSetter and Lookup::unreflectVarHandle 29 */ 30 31 import java.io.IOException; 32 import java.io.UncheckedIOException; 33 import java.lang.invoke.MethodHandle; 34 import java.lang.invoke.MethodHandles; 35 import java.lang.invoke.VarHandle; 36 import java.lang.reflect.Field; 37 import java.lang.reflect.Modifier; 38 import java.nio.file.Files; 39 import java.nio.file.Path; 40 import java.nio.file.Paths; 41 42 import org.testng.annotations.Test; 43 import static org.testng.Assert.*; 44 45 public class UnreflectTest { 46 static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); 47 static final Class<?> hiddenClass = defineHiddenClass(); 48 private static Class<?> defineHiddenClass() { 49 String classes = System.getProperty("test.classes"); 50 Path cf = Paths.get(classes, "Fields.class"); 51 try { 52 byte[] bytes = Files.readAllBytes(cf); 53 return MethodHandles.lookup().defineHiddenClass(bytes, true).lookupClass(); 54 } catch (IOException e) { 55 throw new UncheckedIOException(e); 56 } catch (IllegalAccessException e) { 57 throw new RuntimeException(e); 58 } 59 } 60 61 /* 62 * Test Lookup::unreflectSetter and Lookup::unreflectVarHandle that 63 * can write the value of a non-static final field in a normal class 64 */ 65 @Test 66 public void testFieldsInNormalClass() throws Throwable { 67 // despite the name "HiddenClass", this class is loaded by the 68 // class loader as non-hidden class 69 Class<?> c = Fields.class; 70 Fields o = new Fields(); 71 assertFalse(c.isHiddenClass()); 72 readOnlyAccessibleObject(c, "STATIC_FINAL", null, true); 73 readWriteAccessibleObject(c, "STATIC_NON_FINAL", null, false); 74 readWriteAccessibleObject(c, "FINAL", o, true); 75 readWriteAccessibleObject(c, "NON_FINAL", o, false); 76 } 77 78 /* 79 * Test Lookup::unreflectSetter and Lookup::unreflectVarHandle that 80 * has NO write the value of a non-static final field in a hidden class 81 */ 82 @Test 83 public void testFieldsInHiddenClass() throws Throwable { 84 assertTrue(hiddenClass.isHiddenClass()); 85 Object o = hiddenClass.newInstance(); 86 readOnlyAccessibleObject(hiddenClass, "STATIC_FINAL", null, true); 87 readWriteAccessibleObject(hiddenClass, "STATIC_NON_FINAL", null, false); 88 readOnlyAccessibleObject(hiddenClass, "FINAL", o, true); 89 readWriteAccessibleObject(hiddenClass, "NON_FINAL", o, false); 90 } 91 92 /* 93 * Verify read-only access via unreflectSetter and unreflectVarHandle 94 */ 95 private static void readOnlyAccessibleObject(Class<?> c, String name, Object o, boolean isFinal) throws Throwable { 96 Field f = c.getDeclaredField(name); 97 int modifier = f.getModifiers(); 98 if (isFinal) { 99 assertTrue(Modifier.isFinal(modifier)); 100 } else { 101 assertFalse(Modifier.isFinal(modifier)); 102 } 103 assertTrue(f.trySetAccessible()); 104 105 // Field object with read-only access 106 MethodHandle mh = LOOKUP.unreflectGetter(f); 107 Object value = Modifier.isStatic(modifier) ? mh.invoke() : mh.invoke(o); 108 assertTrue(value == f.get(o)); 109 try { 110 LOOKUP.unreflectSetter(f); 111 assertTrue(false, "should fail to unreflect a setter for " + name); 112 } catch (IllegalAccessException e) { 113 } 114 115 VarHandle vh = LOOKUP.unreflectVarHandle(f); 116 if (isFinal) { 117 assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.SET)); 118 } else { 119 assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.SET)); 120 } 121 } 122 123 private static void readWriteAccessibleObject(Class<?> c, String name, Object o, boolean isFinal) throws Throwable { 124 Field f = c.getDeclaredField(name); 125 int modifier = f.getModifiers(); 126 if (isFinal) { 127 assertTrue(Modifier.isFinal(modifier)); 128 } else { 129 assertFalse(Modifier.isFinal(modifier)); 130 } 131 assertTrue(f.trySetAccessible()); 132 133 // Field object with read-write access 134 MethodHandle mh = MethodHandles.lookup().unreflectGetter(f); 135 Object value = Modifier.isStatic(modifier) ? mh.invoke() : mh.invoke(o); 136 assertTrue(value == f.get(o)); 137 try { 138 MethodHandle setter = MethodHandles.lookup().unreflectSetter(f); 139 if (Modifier.isStatic(modifier)) { 140 setter.invokeExact(value); 141 } else { 142 setter.invoke(o, value); 143 } 144 } catch (IllegalAccessException e) { 145 throw e; 146 } 147 148 VarHandle vh = LOOKUP.unreflectVarHandle(f); 149 if (isFinal) { 150 assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.SET)); 151 } else { 152 assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.SET)); 153 } 154 } 155 }