1 /* 2 * Copyright (c) 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 24 // Android-added: package for test. 25 package test.java.lang.invoke.VarHandles.accessibility; 26 27 /* @test 28 * @bug 8152645 29 * @summary test field lookup accessibility of MethodHandles and VarHandles 30 * @compile TestFieldLookupAccessibility.java 31 * pkg/A.java pkg/B_extends_A.java pkg/C.java 32 * pkg/subpkg/B_extends_A.java pkg/subpkg/C.java 33 * @run testng/othervm TestFieldLookupAccessibility 34 */ 35 36 import org.testng.Assert; 37 import org.testng.annotations.DataProvider; 38 import org.testng.annotations.Test; 39 import pkg.B_extends_A; 40 41 import java.lang.invoke.MethodHandles; 42 import java.lang.reflect.Field; 43 import java.lang.reflect.Modifier; 44 import java.util.ArrayList; 45 import java.util.HashMap; 46 import java.util.HashSet; 47 import java.util.List; 48 import java.util.Map; 49 import java.util.Set; 50 import java.util.stream.Collectors; 51 import java.util.stream.Stream; 52 53 public class TestFieldLookupAccessibility { 54 55 // The set of possible field lookup mechanisms 56 enum FieldLookup { MH_GETTER()57 MH_GETTER() { 58 Object lookup(MethodHandles.Lookup l, Field f) throws Exception { 59 return l.findGetter(f.getDeclaringClass(), f.getName(), f.getType()); 60 } 61 62 boolean isAccessible(Field f) { 63 return !Modifier.isStatic(f.getModifiers()); 64 } 65 }, MH_SETTER()66 MH_SETTER() { 67 Object lookup(MethodHandles.Lookup l, Field f) throws Exception { 68 return l.findSetter(f.getDeclaringClass(), f.getName(), f.getType()); 69 } 70 71 boolean isAccessible(Field f) { 72 return !Modifier.isStatic(f.getModifiers()) && !Modifier.isFinal(f.getModifiers()); 73 } 74 }, MH_STATIC_GETTER()75 MH_STATIC_GETTER() { 76 Object lookup(MethodHandles.Lookup l, Field f) throws Exception { 77 return l.findStaticGetter(f.getDeclaringClass(), f.getName(), f.getType()); 78 } 79 80 boolean isAccessible(Field f) { 81 return Modifier.isStatic(f.getModifiers()); 82 } 83 }, MH_STATIC_SETTER()84 MH_STATIC_SETTER() { 85 Object lookup(MethodHandles.Lookup l, Field f) throws Exception { 86 return l.findStaticSetter(f.getDeclaringClass(), f.getName(), f.getType()); 87 } 88 89 boolean isAccessible(Field f) { 90 return Modifier.isStatic(f.getModifiers()) && !Modifier.isFinal(f.getModifiers()); 91 } 92 }, MH_UNREFLECT_GETTER()93 MH_UNREFLECT_GETTER() { 94 Object lookup(MethodHandles.Lookup l, Field f) throws Exception { 95 return l.unreflectGetter(f); 96 } 97 }, MH_UNREFLECT_GETTER_ACCESSIBLE()98 MH_UNREFLECT_GETTER_ACCESSIBLE() { 99 Object lookup(MethodHandles.Lookup l, Field f) throws Exception { 100 return l.unreflectGetter(cloneAndSetAccessible(f)); 101 } 102 }, MH_UNREFLECT_SETTER()103 MH_UNREFLECT_SETTER() { 104 Object lookup(MethodHandles.Lookup l, Field f) throws Exception { 105 return l.unreflectSetter(f); 106 } 107 108 boolean isAccessible(Field f) { 109 return f.isAccessible() || !Modifier.isFinal(f.getModifiers()); 110 } 111 }, MH_UNREFLECT_SETTER_ACCESSIBLE()112 MH_UNREFLECT_SETTER_ACCESSIBLE() { 113 Object lookup(MethodHandles.Lookup l, Field f) throws Exception { 114 return l.unreflectSetter(cloneAndSetAccessible(f)); 115 } 116 }, VH()117 VH() { 118 Object lookup(MethodHandles.Lookup l, Field f) throws Exception { 119 return l.findVarHandle(f.getDeclaringClass(), f.getName(), f.getType()); 120 } 121 122 boolean isAccessible(Field f) { 123 return !Modifier.isStatic(f.getModifiers()); 124 } 125 }, VH_STATIC()126 VH_STATIC() { 127 Object lookup(MethodHandles.Lookup l, Field f) throws Exception { 128 return l.findStaticVarHandle(f.getDeclaringClass(), f.getName(), f.getType()); 129 } 130 131 boolean isAccessible(Field f) { 132 return Modifier.isStatic(f.getModifiers()); 133 } 134 }, VH_UNREFLECT()135 VH_UNREFLECT() { 136 Object lookup(MethodHandles.Lookup l, Field f) throws Exception { 137 return l.unreflectVarHandle(f); 138 } 139 }; 140 141 // Look up a handle to a field lookup(MethodHandles.Lookup l, Field f)142 abstract Object lookup(MethodHandles.Lookup l, Field f) throws Exception; 143 isAccessible(Field f)144 boolean isAccessible(Field f) { 145 return true; 146 } 147 cloneAndSetAccessible(Field f)148 static Field cloneAndSetAccessible(Field f) throws Exception { 149 // Clone to avoid mutating source field 150 f = f.getDeclaringClass().getDeclaredField(f.getName()); 151 f.setAccessible(true); 152 return f; 153 } 154 } 155 156 @DataProvider lookupProvider()157 public Object[][] lookupProvider() throws Exception { 158 Stream<List<Object>> baseCases = Stream.of( 159 // Look up from same package 160 List.of(pkg.A.class, pkg.A.lookup(), pkg.A.inaccessibleFields()), 161 List.of(pkg.A.class, pkg.A.lookup(), pkg.A.inaccessibleFields()), 162 List.of(pkg.A.class, B_extends_A.lookup(), B_extends_A.inaccessibleFields()), 163 List.of(pkg.A.class, pkg.C.lookup(), pkg.C.inaccessibleFields()), 164 165 // Look up from sub-package 166 List.of(pkg.A.class, pkg.subpkg.B_extends_A.lookup(), pkg.subpkg.B_extends_A.inaccessibleFields()), 167 List.of(pkg.A.class, pkg.subpkg.C.lookup(), pkg.subpkg.C.inaccessibleFields()) 168 ); 169 170 // Cross product base cases with the field lookup classes 171 return baseCases. 172 flatMap(l -> Stream.of(FieldLookup.values()).map(fl -> prepend(fl, l))). 173 toArray(Object[][]::new); 174 } 175 prepend(Object o, List<Object> l)176 private static Object[] prepend(Object o, List<Object> l) { 177 List<Object> pl = new ArrayList<>(); 178 pl.add(o); 179 pl.addAll(l); 180 return pl.toArray(); 181 } 182 183 @Test(dataProvider = "lookupProvider") test(FieldLookup fl, Class<?> src, MethodHandles.Lookup l, Set<String> inaccessibleFields)184 public void test(FieldLookup fl, Class<?> src, MethodHandles.Lookup l, Set<String> inaccessibleFields) { 185 // Add to the expected failures all inaccessible fields due to accessibility modifiers 186 Set<String> expected = new HashSet<>(inaccessibleFields); 187 Map<Field, Throwable> actual = new HashMap<>(); 188 189 for (Field f : fields(src)) { 190 // Add to the expected failures all inaccessible fields due to static/final modifiers 191 if (!fl.isAccessible(f)) { 192 expected.add(f.getName()); 193 } 194 195 try { 196 fl.lookup(l, f); 197 } 198 catch (Throwable t) { 199 // Lookup failed, add to the actual failures 200 actual.put(f, t); 201 } 202 } 203 204 Set<String> actualFieldNames = actual.keySet().stream().map(Field::getName). 205 collect(Collectors.toSet()); 206 if (!actualFieldNames.equals(expected)) { 207 if (actualFieldNames.isEmpty()) { 208 // Setting the accessibility bit of a Field grants access under 209 // all conditions for MethodHander getters and setters 210 if (fl != FieldLookup.MH_UNREFLECT_GETTER_ACCESSIBLE && 211 fl != FieldLookup.MH_UNREFLECT_SETTER_ACCESSIBLE) { 212 Assert.assertEquals(actualFieldNames, expected, "No accessibility failures:"); 213 } 214 } 215 else { 216 Assert.assertEquals(actualFieldNames, expected, "Accessibility failures differ:"); 217 } 218 } 219 else { 220 if (!actual.values().stream().allMatch(IllegalAccessException.class::isInstance)) { 221 Assert.fail("Expecting an IllegalArgumentException for all failures " + actual); 222 } 223 } 224 } 225 fields(Class<?> src)226 static List<Field> fields(Class<?> src) { 227 return List.of(src.getDeclaredFields()); 228 } 229 } 230