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